#ifndef __SESSION_HPP__
#define __SESSION_HPP__
#include "webserver.hpp"
#include <unordered_map>
#include <mutex>
#include <thread>
#include <functional>

typedef enum
{
    SESSION_LOGN,
    SESSION_UNLOGN
} SESSION_STATE;

// session类
class session
{
private:
    SESSION_STATE _state;   // 是否登录
    int _id;                // 标识session
    int _uid;               // 用户id
    server::timer_ptr _ptr; // 定时器任务对象

public:
    session(int session_id) : _id(session_id)
    {
        debug_log("session:%d 创建成功", _id);
    }
    ~session()
    {
        debug_log("session%d 销毁", _id);
    }

    // 设置状态
    void set_statu(SESSION_STATE state) { _state = state; }

    // 设置用户id
    void set_uid(int uid) { _uid = uid; }

    // 设置定时器对象
    void set_timeptr(server::timer_ptr ptr) { _ptr = ptr; }

    // 查看是否在线
    bool is_login() { return _state == SESSION_LOGN; }

    // 返回定时器对象
    server::timer_ptr get_timeptr() { return _ptr; }

    // 返回用用户id
    int get_uid() { return _uid; }

    // 返回session id
    int get_session_id() { return _id; }
};

#define TIME_FOREVER -1
#define TIME_OUT 10000 // 默认过期时间10秒
using session_ptr = std::shared_ptr<session>;
// session管理类
class session_ctl
{
private:
    int _next_sid;                                           // 下一个session_id
    std::unordered_map<int, session_ptr> _id_to_session_ptr; // session_id对session_ptr的映射
    std::mutex _mutex;                                       // 互斥锁
    server *_server;                                         // server对象
public:
    session_ctl(server *ser)
    {
        _server = ser;
        _next_sid = 0;
    }
    ~session_ctl() {}

    session_ptr create_session(int uid, SESSION_STATE state) // 创建session对象
    {
        std::unique_lock<std::mutex> lock(_mutex); // 上锁
        session_ptr ptr = std::make_shared<session>(_next_sid);
        ptr->set_statu(state);
        ptr->set_uid(uid);
        _id_to_session_ptr.insert(std::make_pair(_next_sid, ptr));
        _next_sid++;
        return ptr;
    }

    session_ptr find_session(int sid) // 通过session_id寻找session对象
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _id_to_session_ptr.find(sid);
        if (it == _id_to_session_ptr.end()) // 没找到
        {
            debug_log("没找到session对象");
            return session_ptr();
        }
        return it->second;
    }

    void remove_session(int sid) // 通过session_id删除session对象
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _id_to_session_ptr.erase(sid);
    }

    void add_session(session_ptr ptr)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _id_to_session_ptr.insert(make_pair(ptr->get_session_id(), ptr));
    }

    // 处理,以毫秒为单位
    void set_session_remove(int sid, long timeout)
    {
        session_ptr session_tmp_ptr = find_session(sid);
        if (session_tmp_ptr.get() == nullptr)
        {
            return;
        }

        server::timer_ptr time_ptr = session_tmp_ptr->get_timeptr();

        // 定时器没有任务，设置永久化
        if (time_ptr.get() == nullptr && timeout == TIME_FOREVER)
        {
            return;
        }
        else if (time_ptr.get() == nullptr && timeout != TIME_FOREVER) // 定时器没有任务，设置删除任务(一段时间后删除)
        {
            server::timer_ptr tmp_ptr = _server->set_timer(timeout, std::bind(&session_ctl::remove_session, this, sid));
            session_tmp_ptr->set_timeptr(tmp_ptr);
        }
        else if (time_ptr.get() && TIME_FOREVER == timeout) // 定时器有任务，设置成永久化
        {
            // 删除任务，要先保存
            time_ptr->cancel(); // 删除
            session_tmp_ptr->set_timeptr(server::timer_ptr());
            // 重新添加session和id的映射
            _server->set_timer(0, std::bind(&session_ctl::add_session, this, session_tmp_ptr));
        }
        else if (time_ptr.get() && TIME_FOREVER != timeout) // 定时器有任务，重置时间
        {
            time_ptr->cancel(); // 删除
            // 重新添加session和id的映射
            _server->set_timer(0, std::bind(&session_ctl::add_session, this, session_tmp_ptr));
            server::timer_ptr tmp_ptr = _server->set_timer(timeout, std::bind(&session_ctl::remove_session, this, sid));
            session_tmp_ptr->set_timeptr(tmp_ptr);
        }
    }
};

#endif